/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    cmos_ov7740_drv.c
 * @brief   cmos_0v7740摄像头的驱动文件
 * @author  K
 * @version 0.0.1
 * @date    2021-07-26
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-29   | v0.0.1    | Kcode   | cmos_0v7740摄像头的驱动文件
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/videobuf-core.h>

/*!
 * @brief  Step1：打开cmos_ov7740_fops设备文件
 */
static int cmos_ov7740_open(struct file *file)
{
	return 0;
}

/*!
 * @brief  Step2：查询是否为USB摄像头设备
 *         参考：uvc_v4l2_do_ioctl()
 */
static int cmos_ov7740_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{	
	return 0;
}

/*!
 * @brief  Step3：列举USB摄像头设备所支持的格式format
 *         参考：uvc_fmt()
 */
static int cmos_ov7740_enum_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_fmtdesc *f)
{
	return 0;
}

/*!
 * @brief  Step4：返回当前所使用的格式
 */
static int cmos_ov7740_g_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_format *f)
{
	return 0;
}

/*!
 * @brief  Step5：测试驱动程序是否支持某种格式，强制设定格式
 *         参考：uvc_v4l2_try_format()/myvivi_vidioc_try_fmt_vid_cap()
 */
static int cmos_ov7740_try_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{
	return 0;
}

/*!
 * @brief  Step6：设置所支持的格式
 *         参考：myvivi_vidioc_s_fmt_vid_cap()
 */
static int cmos_ov7740_s_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{
	return 0;
}

/*!
 * @brief  Step7：为该设备申请若干个缓冲区，分配头部信息
 *         参考：uvc_alloc_buffers()
 * @return  正数：返回成功分配内存的大小，负数：分配失败
 */
static int cmos_ov7740_reqbufs(struct file *file,
						void *priv, struct v4l2_requestbuffers *p)
{
	return 0;
}

/*!
 * @brief  Step8：启动数据传输
 *         参考：uvc_video_enable()-->uvc_commit_video()/uvc_init_video()
 * @return 0：成功
 */
static int cmos_ov7740_streamon(struct file *file, 
                                    void *priv, enum v4l2_buf_type t)
{
    return 0;
}

/*!
 * @brief  Step0：关闭设备
 *         参考：uvc_video_enable()-->uvc_commit_video()/uvc_init_video()
 * @return 0：成功
 */
static int cmos_ov7740_streamoff(struct file *file, 
									void *priv, enum v4l2_buf_type t)
{
    return 0;
}
                                    
/*!
 * 分配、设置v4l2_ioctl_ops结构体
 */
static const struct v4l2_ioctl_ops s_cmos_ov7740_ioctl_ops = {
    // 表示它是一个摄像头设备
    .vidioc_querycap      = cmos_ov7740_querycap,

    /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    .vidioc_enum_fmt_vid_cap  = cmos_ov7740_enum_fmt_vid_cap,
    .vidioc_g_fmt_vid_cap     = cmos_ov7740_g_fmt_vid_cap,
    .vidioc_try_fmt_vid_cap   = cmos_ov7740_try_fmt_vid_cap,
    .vidioc_s_fmt_vid_cap     = cmos_ov7740_s_fmt_vid_cap,

    /* 缓冲区操作: 申请 */
    .vidioc_reqbufs       = cmos_ov7740_reqbufs,

    /* 使用的是read方式读数据，/查询/放入队列/取出队列  操作不需要*/
    //.vidioc_querybuf      = cmos_ov7740_querybuf,
    //.vidioc_qbuf          = cmos_ov7740_qbuf,
    //.vidioc_dqbuf         = cmos_ov7740_dqbuf,

    /* 查询/获得/设置属性 */
    //.vidioc_queryctrl     = cmos_ov7740_queryctrl,
    //.vidioc_g_ctrl        = cmos_ov7740_g_ctrl,
    //.vidioc_s_ctrl        = cmos_ov7740_s_ctrl,

    /* 启动/停止 */
    .vidioc_streamon      = cmos_ov7740_streamon,
    .vidioc_streamoff     = cmos_ov7740_streamoff,   
};

/*!
 * 
 * @brief  关闭cmos_ov7740_fops设备文件
 */
static int cmos_ov7740_close(struct file *file)
{
    cmos_ov7740_streamoff(NULL, NULL, 0);
	return 0;
}

/*!
 * @brief  应用程序读出数据函数
 */
static ssize_t cmos_ov7740_read(struct file *file, char __user *buf, 
                                size_t count, loff_t *pos)
{
    return 0;
}

static const struct v4l2_file_operations s_cmos_ov7740_fops = {
	.owner			= THIS_MODULE,
    .open       	= cmos_ov7740_open,
    .release    	= cmos_ov7740_close,
    .unlocked_ioctl = video_ioctl2,
    .read           = cmos_ov7740_read
};

/*
 * @brief  必须的函数，否则在加载驱动时会出错
 * @return 无
 */
static void cmos_ov7740_release(struct video_device *vdev)
{}

/*!
 * 分配、设置video_device结构体
 */
static struct video_device s_cmos_ov7740_vdev = {
    .name      = "cmos_ov7740",
    .release   = cmos_ov7740_release,
    .fops      = &s_cmos_ov7740_fops,
    .ioctl_ops = &s_cmos_ov7740_ioctl_ops,
};

/*
 * @brief  在总线中找到对应的设备文件后就调用probe函数
 * @return  0：成功 其他值：失败
 */
static int __devinit cmos_ov7740_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
    int ret;
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    
    /*! 
     * 注册结构体 
     * -1：自动分配次设备号
     */
    ret = video_register_device(&s_cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1);
    if (ret) {
        printk("unable to register video device (error=%i).\n", ret);
        return ret;
    }
    
	return 0;
}

/*
 * @brief  移除函数
 * @return  0：成功 -1：失败
 */
static int __devexit cmos_ov7740_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/*!
 * 支持设备的名字
 */
static const struct i2c_device_id s_cmos_ov7740_id_table[] = {
	{ "cmos_ov7740", 0 },
	{}
};

/*!
 * 设置i2c设备驱动结构体
 */
static struct i2c_driver s_cmos_ov7740_drv = {
	.driver	= {
		.name	= "cmos_0v7740",
		.owner	= THIS_MODULE,
	},
	.probe		= cmos_ov7740_probe,
	.remove		= __devexit_p(cmos_ov7740_remove),
	.id_table	= s_cmos_ov7740_id_table,
};

/*
 * @brief  cmos_ov7740_dev初始化函数（入口函数）
 * @return  0：成功 -1：失败
 */
static int cmos_ov7740_drv_init(void)
{
     i2c_add_driver(&s_cmos_ov7740_drv);
     return 0;
}

/*
 * @brief  cmos_ov7740_dev退出函数（出口函数）
 * @return  无
 */
static void cmos_ov7740_drv_exit(void)
{
    video_unregister_device(&s_cmos_ov7740_vdev);
    i2c_del_driver(&s_cmos_ov7740_drv);
}

module_init(cmos_ov7740_drv_init);
module_exit(cmos_ov7740_drv_exit);
MODULE_LICENSE("GPL");

